import java.io.*;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;

public class DNSFilter {

    public record FileHeaderConfig(
            String filePath,
            boolean isBlockList,
            String title,
            String description,
            String homepage,
            boolean preserveCustomGroups
    ) {}

    public static void main(String[] args) {
        List<FileHeaderConfig> headerConfigs = new ArrayList<>();
        addConfig(headerConfigs);

        for (FileHeaderConfig config : headerConfigs) {
            if (config.preserveCustomGroups) {
                processCustomGroupFile(config);
            } else {
                processStandardFile(config);
            }
            System.out.println("！！！数据完成整理：" + config.filePath + "！！！");
        }
    }

    private static void addConfig(List<FileHeaderConfig> headerConfigs) {
        // 黑名单（标准分组）
        blocklist(headerConfigs);
        // 白名单（标准分组）
        //allowlist(headerConfigs);
        // 临时白名单（自定义分组）
        //allowlistTemp(headerConfigs);
    }

    private static void blocklist(List<FileHeaderConfig> headerConfigs) {
        headerConfigs.add(new FileHeaderConfig(
                "./blocklist.txt",
                true,
                "黑名单",
                "个人使用 Just for personal using",
                "https://gitee.com/luancx/dnsfilter/raw/master/blocklist.txt",
                false
        ));
    }

    private static void allowlist(List<FileHeaderConfig> headerConfigs) {
        headerConfigs.add(new FileHeaderConfig(
                "./allowlist.txt",
                false,
                "白名单",
                "个人使用 Just for personal using",
                "https://gitee.com/luancx/dnsfilter/raw/master/allowlist.txt",
                false
        ));
    }

    private static void allowlistTemp(List<FileHeaderConfig> headerConfigs) {
        headerConfigs.add(new FileHeaderConfig(
                "./allowlist_temp.txt",
                false,
                "临时允许白名单",
                "由于某些原因，需要临时允许",
                "https://gitee.com/luancx/dnsfilter/raw/master/allowlist_temp.txt",
                true
        ));
    }

    /**
     * 处理标准格式文件（按字母分组）
     */
    private static void processStandardFile(FileHeaderConfig config) {
        Map<Character, Set<String>> domainMap = readDomainsFromStandardFile(config.filePath);
        writeStandardFile(config, domainMap);
    }

    /**
     * 处理自定义分组文件
     */
    private static void processCustomGroupFile(FileHeaderConfig config) {
        Map<String, List<String>> customGroups = readCustomGroupsFromFile(config.filePath);
        writeCustomGroupFile(config, customGroups);
    }

    /**
     * 读取标准格式文件
     */
    private static Map<Character, Set<String>> readDomainsFromStandardFile(String filePath) {
        Map<Character, Set<String>> domainMap = new HashMap<>();
        try (BufferedReader br = new BufferedReader(new FileReader(filePath))) {
            String line;
            while ((line = br.readLine()) != null) {
                line = line.trim();
                if (isCommentOrEmpty(line) || line.startsWith("# >>")) {
                    continue;
                }

                String cleaned = cleanDomainLine(line);
                if (!cleaned.isEmpty()) {
                    domainMap.computeIfAbsent(getFirstLetter(cleaned), k -> new HashSet<>()).add(cleaned);
                }
            }
        } catch (IOException e) {
            System.out.println("读取文件异常: " + e.getMessage());
        }
        return domainMap;
    }

    /**
     * 读取自定义分组文件
     */
    private static Map<String, List<String>> readCustomGroupsFromFile(String filePath) {
        Map<String, List<String>> customGroups = new LinkedHashMap<>();
        String currentGroup = null;

        try (BufferedReader br = new BufferedReader(new FileReader(filePath))) {
            String line;
            while ((line = br.readLine()) != null) {
                line = line.trim();

                // 识别分组标题
                if (line.startsWith("# >>") && line.contains("<<")) {
                    currentGroup = line;
                    customGroups.put(currentGroup, new ArrayList<>());
                    continue;
                }

                // 跳过注释和空行
                if (isCommentOrEmpty(line)) {
                    continue;
                }

                // 添加到当前分组
                if (currentGroup != null) {
                    customGroups.get(currentGroup).add(line);
                }
            }
        } catch (IOException e) {
            System.out.println("读取文件异常: " + e.getMessage());
        }
        return customGroups;
    }

    /**
     * 写入标准格式文件
     */
    private static void writeStandardFile(FileHeaderConfig config, Map<Character, Set<String>> domainMap) {
        int totalCount = domainMap.values().stream().mapToInt(Set::size).sum();

        try (BufferedWriter writer = new BufferedWriter(new FileWriter(config.filePath))) {
            writeFileHeader(writer, config, totalCount);

            List<Character> sortedLetters = new ArrayList<>(domainMap.keySet());
            Collections.sort(sortedLetters);

            for (Character letter : sortedLetters) {
                writer.write("# >> " + letter + " <<\n");
                List<String> sortedDomains = sortDomains(domainMap.get(letter));
                for (String domain : sortedDomains) {
                    writer.write(formatDomainLine(config, domain) + "\n");
                }
                writer.write("\n");
            }
        } catch (IOException e) {
            System.err.println("文件操作失败: " + e.getMessage());
        }
    }

    /**
     * 写入自定义分组文件
     */
    private static void writeCustomGroupFile(FileHeaderConfig config, Map<String, List<String>> customGroups) {
        int totalCount = customGroups.values().stream().mapToInt(List::size).sum();

        try (BufferedWriter writer = new BufferedWriter(new FileWriter(config.filePath))) {
            writeFileHeader(writer, config, totalCount);

            for (Map.Entry<String, List<String>> entry : customGroups.entrySet()) {
                // 写入分组标题
                writer.write(entry.getKey() + "\n");

                // 处理分组内的域名
                Map<Character, List<String>> groupDomains = new TreeMap<>();
                for (String line : entry.getValue()) {
                    String cleaned = cleanDomainLine(line);
                    if (!cleaned.isEmpty()) {
                        char firstLetter = getFirstLetter(cleaned);
                        groupDomains.computeIfAbsent(firstLetter, k -> new ArrayList<>()).add(cleaned);
                    }
                }

                // 按字母顺序处理分组内的域名
                for (Map.Entry<Character, List<String>> domainEntry : groupDomains.entrySet()) {
                    List<String> sortedLines = sortDomainLines(domainEntry.getValue());
                    for (String line : sortedLines) {
                        writer.write(formatDomainLine(config, line) + "\n");
                    }
                }
                writer.write("\n");
            }
        } catch (IOException e) {
            System.err.println("文件操作失败: " + e.getMessage());
        }
    }

    //==================== 工具方法 ====================//
    private static boolean isCommentOrEmpty(String line) {
        return line.isEmpty() || line.startsWith("!") ||
                (line.startsWith("#") && !line.startsWith("# >>"));
    }

    private static String cleanDomainLine(String line) {
        // 只清洗域名部分，保留原始格式标记
        if (line.startsWith("@@||")) {
            line = line.substring(4);
        }
        if (line.startsWith("||")) {
            line = line.substring(2);
        }
        if (line.endsWith("$important")) {
            line = line.substring(0, line.length() - 10);
        }
        if (line.endsWith("^")) {
            line = line.substring(0, line.length() - 1);
        }
        return line;
    }

    private static char getFirstLetter(String domain) {
        String[] parts = domain.split("\\.");
        if (parts.length <= 2) {
            return Character.toUpperCase(domain.charAt(0));
        }
        return Character.toUpperCase(parts[parts.length - 2].charAt(0));
    }

    private static List<String> sortDomains(Collection<String> domains) {
        return domains.stream()
                .sorted(Comparator.comparingInt(String::length).thenComparing(Comparator.naturalOrder()))
                .toList();
    }

    private static List<String> sortDomainLines(List<String> lines) {
        return lines.stream()
                .sorted(Comparator.comparing(line -> {
                    String cleaned = cleanDomainLine(line);
                    return cleaned.length() + cleaned; // 先按长度后按内容排序
                }))
                .toList();
    }

    private static void writeFileHeader(BufferedWriter writer, FileHeaderConfig config, int count) throws IOException {
        writer.write("! Title: " + config.title + "\n");
        writer.write("! Description: " + config.description + "\n");
        writer.write("! Version: " + getCurrentFormattedTime() + "\n");
        writer.write("! Homepage: " + config.homepage + "\n");
        if (config.isBlockList) {
            writer.write("! Blocked domains: " + count + "\n");
        }
        writer.write("!\n");
        writer.write("!----------------------------------------------------------------------------------------------------------------------\n");
        writer.write("!\n");
    }

    private static String formatDomainLine(FileHeaderConfig config, String domain) {
        return config.isBlockList ? "||" + domain + "^" : "@@||" + domain + "^";
    }

    private static String getCurrentFormattedTime() {
        return LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmm"));
    }
}